import React, { useState, useRef, useMemo, useEffect, useContext } from 'react';
import { Switch, Route, Redirect, useHistory } from 'react-router-dom';
import { Layout, Menu, Breadcrumb, Spin, Badge } from '@arco-design/web-react';
import cs from 'classnames';
import { IconMenuFold, IconMenuUnfold } from '@arco-design/web-react/icon';
import { useSelector } from 'react-redux';
import qs from 'query-string';
import NProgress from 'nprogress';
import Navbar from './components/NavBar';
import Footer from './components/Footer';
import useRoute, { IRoute } from '@/routes';
import useLocale from './utils/useLocale';
import getUrlParams from './utils/getUrlParams';
import lazyload from './utils/lazyload';
import store, { GlobalState } from './store';
import styles from './style/layout.module.less';
import { GlobalContext } from './context';

import Dashboard from '@/assets/dashboard.svg';
import Authentication from '@/assets/Authentication.svg';
import Entities from '@/assets/Entities.svg';
import Officer from '@/assets/Officer.svg';
import Task from '@/assets/task.svg';
import Violation from '@/assets/Violation.svg';
import Request from '@/assets/Request.svg';
import Device from '@/assets/Device.svg';
import checkLogin from './utils/checkLogin';
import { remove } from './utils/storage';
import { Notice } from './api/modules/myTask';
import eventBus from '@/utils/EventBus';

const MenuItem = Menu.Item;
const SubMenu = Menu.SubMenu;

const Sider = Layout.Sider;
const Content = Layout.Content;

NProgress.settings.showSpinner = false;

function getIconFromKey(key, rtl) {
  const className = cs(rtl ? styles['rtl-icon'] : styles.icon, 'arco-icon');
  switch (key) {
    case 'menu.dashboard':
      return <Dashboard className={className} />;
    case 'menu.myTasks':
      return <Task className={className} />;
    case 'menu.violation':
      return <Violation className={className} />;
    case 'menu.entities':
      return <Entities className={className} />;
    case 'menu.entityProfiles':
      return <Officer className={className} />;
    case 'menu.authentication':
      return <Authentication className={className} />;
    case 'menu.otprequest':
      return <Request className={className} />;
    case 'menu.deviceManager':
      return <Device className={className} />;
    default:
      return <div className={styles['icon-empty']} />;
  }
}

/**
 * 获取扁平化的路由数组
 * @param routes 路由配置数组
 * @returns 扁平化的路由数组
 */
function getFlattenRoutes(routes) {
  const res = [];
  function travel(_routes) {
    _routes.forEach((route) => {
      const visibleChildren = (route.children || []).filter(
        (child) => !child.ignore
      );
      if (route.key && (!route.children || !visibleChildren.length)) {
        route.key = route.key;
        try {
          res.push(route);
        } catch (_) {}
      }
      if (route.children && route.children.length) {
        travel(route.children);
      }
    });
  }
  travel(routes);
  return res;
}

function PageLayout() {
  const urlParams = getUrlParams();
  const history = useHistory();
  const pathname = history.location.pathname;
  const currentComponent = qs.parseUrl(pathname).url.slice(1);
  const locale = useLocale();
  const {
    settings,
    userLoading,
    settings: { collapsed },
    meuns,
    token,
  } = useSelector((state: GlobalState) => state);

  const [routes, defaultRoute] = useRoute(meuns);
  const defaultSelectedKeys = [currentComponent || defaultRoute];
  const paths = (currentComponent || defaultRoute).split('/');
  const defaultOpenKeys = paths.slice(0, paths.length - 1);

  const [breadcrumb, setBreadCrumb] = useState([]);

  const setCollapsed = () => {
    store.dispatch({
      type: 'update-setting-collapsed',
      payload: { collapsed: !settings.collapsed },
    });
  };

  const [selectedKeys, setSelectedKeys] =
    useState<string[]>(defaultSelectedKeys);
  const [openKeys, setOpenKeys] = useState<string[]>(defaultOpenKeys);

  const routeMap = useRef<Map<string, React.ReactNode[]>>(new Map());
  const menuMap = useRef<
    Map<string, { menuItem?: boolean; subMenu?: boolean }>
  >(new Map());

  const navbarHeight = 60;
  const menuWidth = collapsed ? 48 : settings.menuWidth;

  const showNavbar = settings.navbar && urlParams.navbar !== false;
  const showMenu = settings.menu && urlParams.menu !== false;
  const showFooter = settings.footer && urlParams.footer !== false;

  const flattenRoutes = useMemo(() => getFlattenRoutes(routes) || [], [routes]);

  const [noticeNum, setNoticeNum] = useState<number>(0);

  let timer: NodeJS.Timeout;
  const getNoticeNum = async () => {
    try {
      const { data } = await Notice();
      setNoticeNum(data);
    } finally {
      if (timer) {
        clearTimeout(timer);
      }
      timer = setTimeout(() => {
        getNoticeNum();
      }, 15000);
    }
  };
  useEffect(() => {
    if (checkLogin()) {
      getNoticeNum();
      eventBus.on('CLICKNOTICE', getNoticeNum);
    } else {
      if (timer) {
        clearTimeout(timer);
      }
      eventBus.off('CLICKNOTICE', getNoticeNum);
    }
    return () => {
      if (timer) {
        clearTimeout(timer);
      }
      eventBus.off('CLICKNOTICE', getNoticeNum);
    };
  }, [token]);

  /**
   * 渲染路由
   * @param locale - 语言包
   * @returns 渲染路由的函数
   */
  function renderRoutes(locale: { [x: string]: any }) {
    routeMap.current.clear(); // 清空路由映射

    /**
     * 遍历路由并生成菜单项
     * @param _routes - 路由数组
     * @param level - 菜单层级
     * @param parentNode - 父菜单数组
     * @returns 生成的菜单项
     */
    return function travel(_routes: IRoute[], level, parentNode = []) {
      return _routes.map((route) => {
        const { breadcrumb = true, ignore } = route;
        const iconDom = getIconFromKey(route.name, rtl); // 根据路由键获取图标元素
        const titleDom = (
          <>
            {route.name === 'menu.myTasks' ? (
              !collapsed ? (
                <Badge
                  maxCount={99}
                  count={noticeNum}
                  dotStyle={{
                    transform: 'scale(0.8)',
                    fontFamily: 'auto',
                    boxShadow: 'none',
                  }}
                  offset={rtl ? [0, -9] : [18, -9]}
                >
                  {iconDom} {locale[route.name] || route.name}
                </Badge>
              ) : (
                <>
                  {iconDom}
                  {locale[route.name] || route.name}
                </>
              )
            ) : (
              <>
                {iconDom} {locale[route.name] || route.name}
              </>
            )}
          </>
        );

        // 根据路由是否为面包屑菜单，更新路由映射
        routeMap.current.set(
          `/${route.key}`,
          breadcrumb ? [...parentNode, route.name] : []
        );

        // 过滤掉不需要显示的子路由并继续遍历子路由
        const visibleChildren = (route.children || []).filter((child) => {
          const { ignore, breadcrumb = true } = child;
          if (ignore || route.ignore) {
            routeMap.current.set(
              `/${child.key}`,
              breadcrumb ? [...parentNode, route.name, child.name] : []
            );
          }

          return !ignore;
        });

        // 如果路由被忽略，则返回空字符串
        if (ignore) {
          return '';
        }

        // 如果有子路由，则生成子菜单
        if (visibleChildren.length) {
          menuMap.current.set(route.key, { subMenu: true });
          return (
            <SubMenu key={route.key} title={titleDom}>
              {travel(visibleChildren, level + 1, [...parentNode, route.name])}
            </SubMenu>
          );
        }

        // 如果没有子路由，则生成菜单项
        menuMap.current.set(route.key, { menuItem: true });
        return (
          <MenuItem
            key={route.key}
            renderItemInTooltip={() => <>{locale[route.name] || route.name}</>}
          >
            {titleDom}
          </MenuItem>
        );
      });
    };
  }

  function onClickMenuItem(key: any) {
    remove('myTask_pagination');
    remove('myTask_formParams');
    remove('violation_pagination');
    remove('violation_formParams');
    const currentRoute = flattenRoutes.find((r) => r.key === key);
    if (!currentRoute.component) {
      history.replace(`/${key}`);
      return;
    }
    const component = currentRoute.component;
    const preload = component.preload();

    preload.then(() => {
      history.push(currentRoute.path ? currentRoute.path : `/${key}`);
      history.push(`/${key}`);
      NProgress.done();
    });
  }

  function toggleCollapse() {
    setCollapsed();
  }
  const { rtl } = useContext(GlobalContext);

  const paddingLeft = showMenu
    ? rtl
      ? { paddingRight: menuWidth }
      : { paddingLeft: menuWidth }
    : {};
  const paddingTop = showNavbar ? { paddingTop: navbarHeight } : {};
  const paddingStyle = { ...paddingLeft, ...paddingTop };

  function updateMenuStatus() {
    const pathKeys = pathname.split('/');
    const newSelectedKeys: string[] = [];
    const newOpenKeys: string[] = [...openKeys];
    while (pathKeys.length > 0) {
      const currentRouteKey = pathKeys.join('/');
      const menuKey = currentRouteKey.replace(/^\//, '');
      const menuType = menuMap.current.get(menuKey);
      if (menuType && menuType.menuItem) {
        newSelectedKeys.push(menuKey);
      }
      if (menuType && menuType.subMenu && !openKeys.includes(menuKey)) {
        newOpenKeys.push(menuKey);
      }
      pathKeys.pop();
    }
    setSelectedKeys(newSelectedKeys);
    setOpenKeys(newOpenKeys);
  }

  useEffect(() => {
    const routeConfig = routeMap.current.get(pathname);
    setBreadCrumb(routeConfig || []);
    updateMenuStatus();
    window.scrollTo({
      top: 0,
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [pathname]);

  useEffect(() => {
    if (!checkLogin()) {
      history.replace('/login');
    }
  }, [token]);

  return (
    <Layout className={cs(styles.layout, rtl ? 'rtl' : '')}>
      <div
        className={cs(styles['layout-navbar'], {
          [styles['layout-navbar-hidden']]: !showNavbar,
        })}
      >
        <Navbar show={showNavbar} leftWidth={menuWidth} collapsed={collapsed} />
      </div>
      {userLoading ? (
        <Spin className={styles['spin']} />
      ) : (
        <Layout>
          {showMenu && (
            <Sider
              className={cs(
                styles['layout-sider'],
                rtl ? styles['layout-side-arEG'] : styles['layout-side-other']
              )}
              width={menuWidth}
              collapsed={collapsed}
              onCollapse={setCollapsed}
              trigger={null}
              collapsible
              breakpoint="xl"
              style={paddingTop}
            >
              <div className={styles['menu-wrapper']}>
                <Menu
                  mode="vertical"
                  collapse={collapsed}
                  onClickMenuItem={onClickMenuItem}
                  selectedKeys={selectedKeys}
                  openKeys={openKeys}
                  onClickSubMenu={(_, openKeys) => setOpenKeys(openKeys)}
                >
                  {renderRoutes(locale)(routes, 1)}
                </Menu>
              </div>
              <div className={styles['collapse-btn']} onClick={toggleCollapse}>
                {collapsed ? <IconMenuUnfold /> : <IconMenuFold />}
              </div>
            </Sider>
          )}
          <Layout className={styles['layout-content']} style={paddingStyle}>
            <div className={styles['layout-content-wrapper']}>
              {!!breadcrumb.length && (
                <div
                  className={cs(styles['layout-breadcrumb'], rtl ? 'rtl' : '')}
                >
                  <Breadcrumb>
                    {breadcrumb.map((node, index) => (
                      <Breadcrumb.Item key={index}>
                        {typeof breadcrumb[0] === 'string' && index === 0 ? (
                          <div
                            style={{
                              [rtl ? 'marginLeft' : 'marginRight']: '8px',
                            }}
                          >
                            {getIconFromKey(breadcrumb[0], rtl)}
                          </div>
                        ) : (
                          ''
                        )}
                        {typeof node === 'string' ? locale[node] || node : node}
                      </Breadcrumb.Item>
                    ))}
                  </Breadcrumb>
                </div>
              )}
              <Content>
                <Switch>
                  {flattenRoutes.map((route, index) => {
                    const routeDom = !route.component ? (
                      <Route exact key={index} path={`/${route.key}`}>
                        <Redirect to={`/${route.children[0].key}`} />
                      </Route>
                    ) : (
                      <Route
                        key={index}
                        path={`/${route.key}`}
                        component={route.component}
                      />
                    );
                    return routeDom;
                  })}
                  <Route exact path="/">
                    <Redirect to={`/${defaultRoute}`} />
                  </Route>
                  <Route
                    path="*"
                    component={lazyload(() => import('./pages/exception/403'))}
                  />
                </Switch>
              </Content>
            </div>
            {showFooter && <Footer />}
          </Layout>
        </Layout>
      )}
    </Layout>
  );
}

export default PageLayout;
